#include "pcap_handler.h"

PcapHandler::PcapHandler() {
}

int PcapHandler::Init(int DeviceNum, std::string Filter) {
	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum;
	int i=0;
	char errbuf[PCAP_ERRBUF_SIZE];
	u_int netmask;
	struct bpf_program fcode;

	/* Retrieve the device list */
	if (pcap_findalldevs_ex(const_cast<char*>(PCAP_SRC_IF_STRING), NULL, &alldevs, errbuf) == -1) {
		std::cerr << "Error in pcap_findalldevs: " << errbuf << std::endl;
		exit(1);
	}
	
	if (DeviceNum == 0) {
		/* Print the list */
		std::cout << "Please choose your network interface:\n\n";
		for(d=alldevs; d; d=d->next) {
			std::cout << ++i << ". " << d->name << std::endl;
			if (d->description)
				std::cout << d->description << std::endl << std::endl;
			else
				std::cout << " (No description available)\n";
		}

		if(i==0) {
			std::cout << "No interfaces found! Make sure WinPcap is installed.\n";
			return false;
		}
		
		std::cout << "Enter the interface number (1-" << i << "):\n";
		std::cin >> inum;
		std::cout << std::endl;
	} else {
		for(d=alldevs; d; d=d->next)
			++i;
		inum=DeviceNum;
	}
	
	if(inum < 1 || inum > i) {
		std::cout << "Interface number out of range.\n";
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}

	/* Jump to the selected adapter */
	for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
	
	/* Open the adapter */
	if ( (adhandle= pcap_open(d->name,  // name of the device
							 65536,     // portion of the packet to capture. 
										// 65536 grants that the whole packet will be captured on all the MACs.
							 PCAP_OPENFLAG_PROMISCUOUS,         // promiscuous mode
							 1000,      // read timeout
							 NULL,      // remote authentication
							 errbuf     // error buffer
							 ) ) == NULL)
	{
		std::cerr << "Unable to open the adapter. Adapter not supported by WinPcap" << std::endl;
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}
	
	/* Check the link layer. We support only Ethernet for simplicity. */
	if(pcap_datalink(adhandle) != DLT_EN10MB)
	{
		std::cerr << "This program works only on Ethernet networks.\n";
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}
	
	if(d->addresses != NULL)
		/* Retrieve the mask of the first address of the interface */
		netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else
		/* If the interface is without addresses we suppose to be in a C class network */
		netmask=0xffffff; 


	//compile the filter
	if (pcap_compile(adhandle, &fcode, const_cast<char *>(Filter.c_str()), 1, netmask) <0 ) {
		std::cerr << "Unable to compile the packet filter. Check the syntax.\n";
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}
	
	//set the filter
	if (pcap_setfilter(adhandle, &fcode)<0) {
		std::cerr << "Error setting the filter.\n";
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}
	
	std::cout << "*** Listening on " << d->description << "...\n" ;
	std::cout << "Packets filtered for \"" << Filter << "\":\n\n";
	/* At this point, we don't need any more the device list. Free it */
	pcap_freealldevs(alldevs);
	
	// Success.
	return true;
}

int PcapHandler::StartCaptureLoop() {
	enum CaptureErrors {
	   	READING_FAILED,
		PARSING_FAILED,
		VIRTUAL_FUNC_NOT_DEFINED
	};
	// Start the capture loop
	while (1) {
		try {
			int Result = pcap_next_ex(adhandle, &Packet.header, &Packet.data);
			// Timeout elapsed
			if (Result==0)
				continue;
			// Error reading the packet.
			else if (Result == -1) {
				throw READING_FAILED;
			}
			int ParseReturn = ParsePacket();
			if (ParseReturn == 0)
				throw PARSING_FAILED;
			else if (ParseReturn == -1)
				throw VIRTUAL_FUNC_NOT_DEFINED;
		}
		catch (CaptureErrors Error) {
			// Close the capture device.
			pcap_close(adhandle);
			switch (Error) {
				case READING_FAILED :
					std::cerr << "Error reading packet.\n";
					return 0;
				case PARSING_FAILED :
					std::cerr << "Error parsing packet.\n";
					return 0;
				case VIRTUAL_FUNC_NOT_DEFINED :
					std::cerr << "Error: Please derive PcapHandler class and "
					          << "define its virtual function ParsePacket().\n";
					return 0;
			}
		}
	}
	return 1;
}

int PcapHandler::ParsePacket() {
	return -1;
}

void PcapHandler::Print(const u_char *data, unsigned int length) {
	for (unsigned int i=1; i<=length; ++i) {
		printf("%.2x ", data[i-1]);
		if (i==length) {
			for (unsigned int j=0;j<(16-length%16)*3; ++j)
				std::cout << " ";
			std::cout << "\t";
			for (unsigned int j=length-length%16; j<length; ++j) {
				int CurrentChar=data[j-1];
				if (CurrentChar>=32 && CurrentChar<=126)
					std::cout << static_cast<char>(CurrentChar);
				else
					std::cout << ".";
			}
		} else if (i%16==0) {
			std::cout << "\t";
			for (unsigned int j=i-16; j<i; ++j) {
				int CurrentChar=data[j-1];
				if (CurrentChar>=32 && CurrentChar<=126)
					std::cout << static_cast<char>(CurrentChar);
				else
					std::cout << ".";
			}
			std::cout << std::endl;
		}
		else if (i%8==0)
			std::cout << " ";
	}
	std::cout << std::endl << std::endl;
}

